"""
WAL (Write-Ahead Logging) 类型定义
"""

import struct
from enum import Enum
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, List, Dict, Any
import hashlib


class WALRecordType(Enum):
    """WAL记录类型"""
    BEGIN = 1           # 事务开始
    COMMIT = 2          # 事务提交
    ROLLBACK = 3        # 事务回滚
    INSERT = 4          # 插入操作
    UPDATE = 5          # 更新操作
    DELETE = 6          # 删除操作
    CHECKPOINT = 7      # 检查点
    ABORT = 8          # 事务中止


@dataclass
class WALRecord:
    """WAL日志记录"""
    lsn: int                        # 日志序列号 (Log Sequence Number)
    transaction_id: str             # 事务ID
    record_type: WALRecordType      # 记录类型
    timestamp: datetime             # 时间戳
    table_name: Optional[str] = None       # 表名 (对于数据操作)
    page_id: Optional[int] = None          # 页面ID
    offset: Optional[int] = None           # 页内偏移
    before_image: Optional[bytes] = None   # 修改前数据 (用于Undo)
    after_image: Optional[bytes] = None    # 修改后数据 (用于Redo)
    checksum: Optional[int] = None         # 校验和
    
    def __post_init__(self):
        """计算校验和"""
        if self.checksum is None:
            self.checksum = self._calculate_checksum()
    
    def _calculate_checksum(self) -> int:
        """计算记录的校验和"""
        data = []
        data.append(str(self.lsn).encode())
        data.append(self.transaction_id.encode())
        data.append(str(self.record_type.value).encode())
        data.append(str(self.timestamp.timestamp()).encode())
        
        if self.table_name:
            data.append(self.table_name.encode())
        if self.page_id is not None:
            data.append(str(self.page_id).encode())
        if self.offset is not None:
            data.append(str(self.offset).encode())
        if self.before_image:
            data.append(self.before_image)
        if self.after_image:
            data.append(self.after_image)
        
        combined = b''.join(data)
        return int(hashlib.md5(combined).hexdigest()[:8], 16)
    
    def verify_checksum(self) -> bool:
        """验证校验和"""
        expected = self._calculate_checksum()
        return self.checksum == expected
    
    def serialize(self) -> bytes:
        """序列化WAL记录为二进制格式"""
        # 准备数据
        tx_id_bytes = self.transaction_id.encode('utf-8')
        table_name_bytes = self.table_name.encode('utf-8') if self.table_name else b''
        before_image = self.before_image if self.before_image else b''
        after_image = self.after_image if self.after_image else b''
        
        # 计算总长度
        header_size = 32  # 固定头部大小
        variable_size = (
            4 + len(tx_id_bytes) +          # 事务ID长度 + 事务ID
            4 + len(table_name_bytes) +     # 表名长度 + 表名
            4 + 4 +                         # page_id + offset
            4 + len(before_image) +         # before_image长度 + before_image
            4 + len(after_image) +          # after_image长度 + after_image
            4                               # 校验和
        )
        total_length = header_size + variable_size
        
        # 构建头部 (32字节)
        header = struct.pack(
            'QQII8s',
            self.lsn,                                    # 8字节：LSN
            int(self.timestamp.timestamp() * 1000000),   # 8字节：时间戳(微秒)
            total_length,                                # 4字节：总长度
            self.record_type.value,                      # 4字节：记录类型
            b'\x00' * 8                                  # 8字节：保留字段
        )
        
        # 构建可变部分
        variable_data = []
        
        # 事务ID
        variable_data.append(struct.pack('I', len(tx_id_bytes)))
        variable_data.append(tx_id_bytes)
        
        # 表名
        variable_data.append(struct.pack('I', len(table_name_bytes)))
        variable_data.append(table_name_bytes)
        
        # 页面ID和偏移
        variable_data.append(struct.pack('I', self.page_id if self.page_id is not None else 0))
        variable_data.append(struct.pack('I', self.offset if self.offset is not None else 0))
        
        # Before Image
        variable_data.append(struct.pack('I', len(before_image)))
        variable_data.append(before_image)
        
        # After Image
        variable_data.append(struct.pack('I', len(after_image)))
        variable_data.append(after_image)
        
        # 校验和
        variable_data.append(struct.pack('I', self.checksum))
        
        return header + b''.join(variable_data)
    
    @classmethod
    def deserialize(cls, data: bytes) -> 'WALRecord':
        """从二进制数据反序列化WAL记录"""
        if len(data) < 32:
            raise ValueError("数据长度不足，无法解析WAL记录头部")
        
        # 解析头部
        lsn, timestamp_us, total_length, record_type_val = struct.unpack('QQII', data[:24])
        
        if len(data) < total_length:
            raise ValueError(f"数据长度不足，期望 {total_length} 字节，实际 {len(data)} 字节")
        
        # 转换时间戳
        timestamp = datetime.fromtimestamp(timestamp_us / 1000000)
        record_type = WALRecordType(record_type_val)
        
        # 解析可变部分
        offset = 32  # 跳过头部
        
        # 事务ID
        tx_id_len = struct.unpack('I', data[offset:offset+4])[0]
        offset += 4
        transaction_id = data[offset:offset+tx_id_len].decode('utf-8')
        offset += tx_id_len
        
        # 表名
        table_name_len = struct.unpack('I', data[offset:offset+4])[0]
        offset += 4
        table_name = data[offset:offset+table_name_len].decode('utf-8') if table_name_len > 0 else None
        offset += table_name_len
        
        # 页面ID和偏移
        page_id = struct.unpack('I', data[offset:offset+4])[0]
        page_id = page_id if page_id > 0 else None
        offset += 4
        
        page_offset = struct.unpack('I', data[offset:offset+4])[0]
        page_offset = page_offset if page_offset > 0 else None
        offset += 4
        
        # Before Image
        before_len = struct.unpack('I', data[offset:offset+4])[0]
        offset += 4
        before_image = data[offset:offset+before_len] if before_len > 0 else None
        offset += before_len
        
        # After Image
        after_len = struct.unpack('I', data[offset:offset+4])[0]
        offset += 4
        after_image = data[offset:offset+after_len] if after_len > 0 else None
        offset += after_len
        
        # 校验和
        checksum = struct.unpack('I', data[offset:offset+4])[0]
        
        return cls(
            lsn=lsn,
            transaction_id=transaction_id,
            record_type=record_type,
            timestamp=timestamp,
            table_name=table_name,
            page_id=page_id,
            offset=page_offset,
            before_image=before_image,
            after_image=after_image,
            checksum=checksum
        )


@dataclass
class WALControlInfo:
    """WAL控制信息"""
    current_wal_file: str           # 当前WAL文件名
    next_lsn: int                   # 下一个LSN
    last_checkpoint_lsn: int        # 最后检查点LSN
    last_checkpoint_time: datetime  # 最后检查点时间
    wal_file_size: int             # WAL文件大小限制
    database_version: str           # 数据库版本


@dataclass
class CheckpointInfo:
    """检查点信息"""
    checkpoint_lsn: int                         # 检查点LSN
    checkpoint_time: datetime                   # 检查点时间
    flushed_pages: List[Dict[str, Any]]        # 已刷新的页面信息
    active_transactions: List[str]              # 活跃事务列表
    wal_files_before_checkpoint: List[str]      # 检查点前的WAL文件列表


class WALConstants:
    """WAL常量"""
    DEFAULT_WAL_FILE_SIZE = 64 * 1024 * 1024    # 64MB
    WAL_FILE_PREFIX = "wal_"
    WAL_FILE_EXTENSION = ".log"
    WAL_CONTROL_FILE = "wal.control"
    WAL_CHECKPOINT_FILE = "wal.checkpoint"
    WAL_DIRECTORY = "wal"
    
    # WAL记录最大大小 (16MB)
    MAX_WAL_RECORD_SIZE = 16 * 1024 * 1024
    
    # 检查点间隔
    CHECKPOINT_INTERVAL_SECONDS = 300  # 5分钟
    CHECKPOINT_INTERVAL_RECORDS = 10000  # 10000条记录